/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections;
using System.Runtime.InteropServices;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Cobalt.Framework.Graphics
{

	public class UserGeometryRenderer<T> : GeometrySource
		where T : struct
	{
		private GraphicsDevice device;
		private VertexDeclaration declaration;
		private int stride;

		public UserGeometryRenderer( GraphicsDevice device, VertexDeclaration declaration )
		{
			this.device = device;
			this.declaration = declaration;
			this.stride = declaration.GetVertexStrideSize( 0 );
		}

		public UserGeometryRenderer( GraphicsDevice device, VertexElement[] declarationElements )
			: this( device, new VertexDeclaration( device, declarationElements ) )
		{
		}

		public GraphicsDevice Device
		{
			get { return device; }
		}

		public StaticGeometryBatch<T> CreateStaticBatch( PrimitiveType topology, T[] vertices )
		{
			if( vertices == null )
				throw new ArgumentNullException();
			if( vertices.Length == 0 )
				throw new ArgumentException();

			return new StaticGeometryBatch<T>( this, topology, vertices, stride );
		}

		public DynamicGeometryBatch<T> CreateDynamicBatch( PrimitiveType topology, int capacity )
		{
			if( capacity < 0 )
				throw new ArgumentOutOfRangeException();

			return new DynamicGeometryBatch<T>( this, topology, capacity );
		}

		protected internal override void DrawSurfaces( DrawRecord[] surfaces, int index, int count, ShadingContext context, IPrerenderHandler prerenderHandler )
		{
			device.VertexDeclaration = declaration;
			for( int i = 0; i < count; i++ )
				(surfaces[index + i].surface as UserGeometryBatch<T>).Render( context );
		}
	}

	public abstract class UserGeometryBatch<T> : GeometrySurface
		where T : struct
	{
		internal UserGeometryBatch( UserGeometryRenderer<T> owner )
			: base( owner )
		{
		}

		internal abstract void Render( ShadingContext context );
	}

	public class StaticGeometryBatch<T> : UserGeometryBatch<T>, IDisposable
		where T : struct
	{
		private int primCount;
		private int stride;
		private VertexBuffer vb;
		private PrimitiveType topology;

		internal StaticGeometryBatch( UserGeometryRenderer<T> owner, PrimitiveType topology, T[] vertices, int stride )
			: base( owner )
		{
			primCount = Helpers.ElemsToPrims( topology, vertices.Length );
			this.stride = stride;
			this.topology = topology;

			vb = new VertexBuffer( owner.Device, vertices.Length * stride, BufferUsage.Points );
			vb.SetData( vertices );

			SurfaceSort = 0;
		}

		internal override void Render( ShadingContext context )
		{
			GraphicsDevice device = context.GraphicsDevice;
			device.Vertices[0].SetSource( vb, 0, stride );
			device.DrawPrimitives( topology, 0, primCount );
		}

		#region IDisposable Members

		public void Dispose()
		{
			Helpers.DisposeAndNull( ref vb );
		}

		#endregion
	}

	public class DynamicGeometryBatch<T> : UserGeometryBatch<T>
		where T : struct
	{
		private T[] verts;
		private PrimitiveType topology;

		internal DynamicGeometryBatch( UserGeometryRenderer<T> owner, PrimitiveType topology, int numPoints )
			: base( owner )
		{
			this.topology = topology;
			verts = new T[numPoints];

			SurfaceSort = 1;
		}

		private int count;
		public T[] GetPointBuffer( int count )
		{
			if( count < 0 )
				throw new ArgumentOutOfRangeException();

			if( count > verts.Length )
				Array.Resize( ref verts, count );

			this.count = count;
			return verts;
		}

		internal override void Render( ShadingContext context )
		{
			if( count > 0 )
				context.GraphicsDevice.DrawUserPrimitives( topology, verts, 0, Helpers.ElemsToPrims( topology, count ) );
		}
	}
}